/***************************************************************
 * Name:      QtHelper
 * Purpose:   Code::Blocks plugin
 * Author:    Sviridov Michael Alexandrovich (2mihail@mail.ru)
 * Created:   2008-09-20
 * Copyright: Sviridov Michael Alexandrovich
 * License:   GPL
 **************************************************************/

#include <sdk.h> // Code::Blocks SDK
#include <configurationpanel.h>
#include "QtHelper.h"
#include <annoyingdialog.h>
#include "QtPrjDlg.h"
#include "QtCfgDlg.h"
#include "QtPrjGen.h"
#include <iostream>
#include "debug_msg.h"
//-------------------------------------------------
// Register the plugin with Code::Blocks.
// We are using an anonymous namespace so we don't litter the global one.
namespace
{
    PluginRegistrant<QtHelper> reg(_T("QtHelper"));
}
//-------------------------------------------------
int wx_proc_id = wxNewId();
//-------------------------------------------------
BEGIN_EVENT_TABLE(QtHelper, cbPlugin)
    EVT_PIPEDPROCESS_TERMINATED(wx_proc_id, QtHelper::OnTerm)
END_EVENT_TABLE()
//-------------------------------------------------
QtHelper::QtHelper() : proc(0), proc_id(0)
{
	if(!Manager::LoadResource(_T("QtHelper.zip")))
		NotifyMissingFile(_T("QtHelper.zip"));
}
//-------------------------------------------------
void QtHelper::OnAttach()
{
	// We handle build start
	Manager::Get()->RegisterEventSink(cbEVT_COMPILER_STARTED, new cbEventFunctor<QtHelper, CodeBlocksEvent>(this, &QtHelper::OnBuildStarted));
}
//-------------------------------------------------
void QtHelper::OnRelease(bool appShutDown)
{
	if(proc_id)
		wxProcess::Kill(proc_id, wxSIGTERM);
}
//-------------------------------------------------
cbConfigurationPanel* QtHelper::GetConfigurationPanel(wxWindow* parent)
{
	return new QtCfgDlg(parent);
}
//-------------------------------------------------
cbConfigurationPanel* QtHelper::GetProjectConfigurationPanel(wxWindow* parent, cbProject* project)
{
	return new QtPrjDlg(parent, project);
}
//-------------------------------------------------
void QtHelper::OnBuildStarted(CodeBlocksEvent& event)
{
	dbg_msg(_T("QtHelper> OnBuildStarted"));

	// build already started
	if(proc_id)
	{
		dbg_err(_T("QtHelper> building already started"));
		return;
	}

	cbProject *prj = event.GetProject();
	// no project selected - ignoring
	if(!prj)
	{
		dbg_err(_T("QtHelper> no project supplied"));
		return;
	}

	// extracting setting that allows to use this plugin
	TiXmlElement *node = prj->GetExtensionsNode()->FirstChildElement("qt_helper");
	bool enabled = false;

	// checking it's state
	if(node)
	{
		TiXmlElement *prm = node->FirstChildElement("enabled");
		if(prm && cbC2U(prm->Attribute("value")) == cbC2U("true"))
			enabled = true;
	}

	// this is not a Qt project
	if(!enabled)
		return;

	dbg_msg(_T("QtHelper> WORK STARTED..."));

	if(!prj->IsMakefileCustom())
		dbg_warn(_T("QtHelper> please, enable using of custom makefile"));

	QtPrjGen gen;
	dbg_msg(_T("QtHelper> creating .pro file..."));

	wxString proFileName = prj->GetBasePath() + prj->GetTitle() + _T(".pro");
	clock_t start = clock();
	if(!gen.create_pro(prj, proFileName))
	{
		dbg_err(_T("QtHelper> failed"));
		return;
	}
	dbg_msg(wxString::Format(_T("QtHelper> done in %ld ms"), clock() - start));

	// extracting path to qmake
	ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("qt_helper"));
	wxString qt_dir = cfg->Read(_T("/QTDIR"));

	if(qt_dir.IsEmpty())
	{
		if(!wxGetEnv(_T("QTDIR"),&qt_dir))
		{
			AnnoyingDialog dlg(_("qmake location could not be establised"),
								_(	"You have not specified a Qt installation directory\n"
									"and the QTDIR environmental variable is not set.\n"
									"If qmake is also not located in a directory listed in\n"
									"your PATH environmental variable then invoking qmake will fail."),
									wxART_INFORMATION,AnnoyingDialog::OK,wxID_OK);
				dlg.ShowModal();
				// We will try to run qmake anyway...
		}
	}

	// preparing cmdline for creating makefile
	wxString cmd = qt_dir;
	Manager::Get()->GetMacrosManager()->ReplaceEnvVars(cmd);

	if(!cmd.IsEmpty())
		cmd += _T("/bin/");
	cmd += _T("qmake -makefile");

	wxString mkspec = cfg->Read(_T("/MKSPEC"));

	if(!mkspec.IsEmpty())
		cmd += _T(" -spec ") + mkspec;

	// else the QMAKESPEC env var will be used that is ok.
	cmd += _T(" -o ") + prj->GetMakefile() + _T(" ") + proFileName;

	dbg_msg(_T("QtHelper> creating makefile..."));
	dbg_msg(_T("QtHelper> executing command: ") + cmd);

	proc = new PipedProcess((void**)&proc,
							this,
							wx_proc_id,
							true,
							prj->GetBasePath());
	proc->Redirect();

	start = clock();
	proc_id = wxExecute(cmd, wxEXEC_ASYNC, proc);

	while(proc_id)
	{
		wxMilliSleep(10);
		check_proc_msgs(proc);
		Manager::Yield();
	}

	dbg_msg(wxString::Format(_T("QtHelper> done in %ld ms"), clock() - start));
}
//-------------------------------------------------
void QtHelper::OnTerm(CodeBlocksEvent& event)
{
	delete proc;
	proc = 0;
	proc_id = 0;
}
//-------------------------------------------------
void QtHelper::check_proc_msgs(wxProcess *proc)
{
	static char buf[4096] = "";
	if(proc->IsErrorAvailable())
	{
		wxInputStream *in = proc->GetErrorStream();
		while(in->Read(&buf, sizeof(buf)).LastRead())
			dbg_err(_T("qmake> ") + wxString().FromAscii(buf));
	}

	if(proc->IsInputAvailable())
	{
		wxInputStream *in = proc->GetInputStream();
		while(in->Read(&buf, sizeof(buf)).LastRead())
			dbg_msg(_T("qmake> ") + wxString().FromAscii(buf));
	}
}
//-------------------------------------------------
